package cn.dota2info.elk.dao;

import cn.dota2info.elk.entity.BaseSearchParam;
import cn.dota2info.elk.entity.Book;
import cn.dota2info.elk.utils.MapUtils;

import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

/**
 * java api中Docement APIs的操作
 * @author wwmxd
 * @email qzywyd@163.com
 * @since  2018年07月06日
 */

@Repository
public class ElkDocDao<T> {

    Logger logger = LoggerFactory.getLogger(ElkDocDao.class);
    @Autowired
    private RestHighLevelClient client;

    private MapUtils<T> utils=new MapUtils<T>();

    /**
     * 存入Object至elasticsearch
     *
     * @param o     存入对象
     * @param index 索引名称
     * @param type  类型名称
     * @param id    对象id，null则自动生成
     * @return
     */
    public IndexResponse insertObject(Object o, String index, String type, String id) {
        IndexRequest request = null;
        /* id为空则自动生成*/
        if (id == null) {
            request = new IndexRequest(index, type);
        } else {
            request = new IndexRequest(index, type, id);
        }
        Map<String, Object> jsonMap = new HashMap<>();
        try {
            jsonMap=MapUtils.Object2Map(o);
            Field idField=o.getClass().getDeclaredField("id");
            idField.setAccessible(true);
            Object ids=idField.get(o);
            if(ids!=null){
                request = new IndexRequest(index, type, (String) ids);
            }

        } catch (IllegalAccessException e) {
            logger.error("存入对象解析失败:", e);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        request.timeout(TimeValue.timeValueSeconds(5));
        request.timeout("5s");
        request.source(jsonMap);
        try {
            return client.index(request);
        } catch (IOException e) {
            logger.error("存入elk失败:", e);
        }
        return null;
    }

    /**
     * 获取index值 必须已知id
     * @param index 索引值
     * @param type  类型
     * @param id    id
     * @param c     封装的对象class
     */
    public Object getObject(String index, String type, String id, Class c) {
        GetRequest request = new GetRequest(index, type, id);
        String[] includes = new String[c.getDeclaredFields().length];
        String[] excludes = Strings.EMPTY_ARRAY;
        int i=0;
        Field [] f=c.getDeclaredFields();
        Field.setAccessible(f,false);
        for(Field field:f){
            String name = field.getName();
            if (!name.equals("id")) {
                includes[i++]=name;
            }
        }
        FetchSourceContext fetchSourceContext =
                new FetchSourceContext(true, includes, excludes);
        request.fetchSourceContext(fetchSourceContext);
        try {
            GetResponse getResponse = client.get(request);
            String ids=getResponse.getId();
            String sourceAsString = getResponse.getSourceAsString();
            logger.info("获取到的信息:"+sourceAsString);
            Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
            Object obj = null;
            try {
                obj = MapUtils.map2Object(sourceAsMap,Book.class,ids);
            } catch (Exception e) {
                logger.error("解析失败:",e);
            }

            return obj;
        } catch (IOException e) {
            logger.error("Get查询失败", e);
        }
        return null;
    }

    /**
     * 判断索引下是否存在该id的信息
     * @param index 索引名称
     * @param type  类型
     * @param id    id
     * @return
     */
    public boolean existsObject(String index,String type,String id){
        GetRequest getRequest = new GetRequest(index, type, id);
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        try {
            return client.exists(getRequest);
        } catch (IOException e) {
            logger.error("Get查询失败", e);
        }
        return false;
    }

    /**
     * 删除某索引
     * @param index
     * @param type
     * @param id  id为null则为删除整个索引
     */
    public boolean deleteObject(String index,String type,String id){
        DeleteRequest request =null;
        if(id==null){
            request=new DeleteRequest(index);
        }else {
            request = new DeleteRequest(index, type, id);
        }
        try {
            DeleteResponse deleteResponse = client.delete(request);
            ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                logger.info("存在部分未删除成功");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason();
                    logger.info("删除失败的原因:"+reason);
                    return false;
                }
            }
        } catch (IOException e) {
            logger.error("删除失败:", e);
            return false;
        }
        return true;
    }

    /**
     * 更新对象
     * @param o
     * @param index
     * @param type
     */
    public UpdateResponse updateObject(Object o, String index, String type) {
        UpdateRequest request = null;
        Field[] f = o.getClass().getDeclaredFields();
        Map<String, Object> jsonMap = new HashMap<>();
        try {
            Field.setAccessible(f,true);
            for (Field field : f) {
                String name = field.getName();
                if (name.equals("id")) {
                    String ids= (String) field.get(o);
                    request = new UpdateRequest(index, type, ids);

                }else {
                    jsonMap.put(name,field.get(o));
                }
            }
            jsonMap.put("updated",new Date());
        } catch (IllegalAccessException e) {
            logger.error("存入对象解析失败:", e);
        }
        if(request==null){
            throw new RuntimeException("object中未找到id");
        }
        request.docAsUpsert(true);
        request.doc(jsonMap);
        try {
            return client.update(request);
        } catch (IOException e) {
            logger.error("更新失败:", e);
        }
        return null;
    }

    /**
     * 批处理，这里只做删除批处理
     * @param index
     * @param type
     * @param list
     */
    public void bulkDelete(String index, String type, List<String>list){
        BulkRequest request = new BulkRequest();
        for(String id:list){
            request.add(new DeleteRequest(index,type,id));
        }
        try {
            client.bulk(request);
        } catch (IOException e) {
            logger.error("批量删除失败:", e);
        }

    }

    /**
     * 获取Index下所有的内容（为search api方法，但为了提供全额查询功能写在这里）
     * @param index
     * @param type
     * @param c
     * @throws Exception
     */
   public List<T> getAll(String index, String type, Class c, BaseSearchParam param) throws Exception {
       try {
           List<T> objects=new ArrayList<>();
           SearchRequest searchRequest = new SearchRequest(index);
           searchRequest.types(type);
           SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
           searchSourceBuilder.query(QueryBuilders.matchAllQuery());
           searchSourceBuilder.from(param.getCurrent());
           searchSourceBuilder.size(param.getSize());
           searchRequest.source(searchSourceBuilder);
           SearchResponse searchResponse = client.search(searchRequest);
           return utils.hits2List(searchResponse.getHits(),c);

       } catch (IOException e) {
           logger.error("查询所有失败",e);
       }

        return null;
   }

    public Long getCount(String index, String type, Class c, BaseSearchParam param) throws Exception {
        try {
            List<T> objects=new ArrayList<>();
            SearchRequest searchRequest = new SearchRequest(index);
            searchRequest.types(type);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
            searchRequest.source(searchSourceBuilder);
            searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
            SearchResponse searchResponse = client.search(searchRequest);
            return searchResponse.getHits().getTotalHits();

        } catch (IOException e) {
            logger.error("查询所有失败",e);
        }

        return null;
    }
    //todo MultiGet API 查询一批文档（学习seacher api后再决定是否需要）
    public void MultiGet(String index, String type, List<String>list){
        MultiGetRequest request = new MultiGetRequest();



    }

}
